多线程编程

1. 概述

多线程编程是指在一个程序中同时执行多个线程,每个线程独立运行,可以并行处理不同的任务。多线程编程可以提高程序的响应性和性能,特别是在处理I/O密集型任务或需要并行计算的场景中。

1.1 为什么需要多线程?

  • 提高响应性:在单线程程序中,如果一个任务需要长时间执行,整个程序会阻塞,导致用户界面无响应。多线程可以将这些长时间任务放在后台线程中执行,保持用户界面的响应性。
  • 提高性能:多线程可以充分利用多核处理器的并行处理能力,加快计算密集型任务的执行速度。

1.2 多线程的挑战

  • 线程安全:多个线程同时访问共享资源时,可能会导致数据不一致或竞争条件。
  • 死锁:两个或多个线程互相等待对方释放资源,导致程序无法继续执行。
  • 资源管理:线程的创建和销毁需要消耗系统资源,过多的线程可能导致资源耗尽。

2. C# 中的多线程编程

C# 提供了丰富的多线程编程支持,包括 Thread 类、Task 类、async/await 等。

2.1 使用 Thread 类

Thread 类是 C# 中用于创建和管理线程的基本类。

2.1.1 创建线程

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 创建一个新的线程
        Thread thread = new Thread(new ThreadStart(DoWork));
        
        // 启动线程
        thread.Start();
        
        // 主线程继续执行
        Console.WriteLine("Main thread is running...");
        
        // 等待线程完成
        thread.Join();
        
        Console.WriteLine("Main thread is done.");
    }

    static void DoWork()
    {
        Console.WriteLine("Worker thread is running...");
        Thread.Sleep(2000); // 模拟工作
        Console.WriteLine("Worker thread is done.");
    }
}

2.1.2 传递参数给线程

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 创建一个新的线程,并传递参数
        Thread thread = new Thread(new ParameterizedThreadStart(DoWork));
        
        // 启动线程
        thread.Start("Hello from Main");
        
        // 主线程继续执行
        Console.WriteLine("Main thread is running...");
        
        // 等待线程完成
        thread.Join();
        
        Console.WriteLine("Main thread is done.");
    }

    static void DoWork(object param)
    {
        string message = (string)param;
        Console.WriteLine("Worker thread received: " + message);
        Thread.Sleep(2000); // 模拟工作
        Console.WriteLine("Worker thread is done.");
    }
}

2.2 使用 Task 类

Task 类是 .NET 中用于异步编程的高级抽象,提供了更强大的功能和更好的性能。

2.2.1 创建和启动任务

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        // 创建一个任务
        Task task = Task.Run(() => DoWork());
        
        // 主线程继续执行
        Console.WriteLine("Main thread is running...");
        
        // 等待任务完成
        await task;
        
        Console.WriteLine("Main thread is done.");
    }

    static void DoWork()
    {
        Console.WriteLine("Worker task is running...");
        Thread.Sleep(2000); // 模拟工作
        Console.WriteLine("Worker task is done.");
    }
}

2.2.2 返回任务结果

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        // 创建一个任务并返回结果
        Task<int> task = Task.Run(() => CalculateSum(10, 20));
        
        // 主线程继续执行
        Console.WriteLine("Main thread is running...");
        
        // 等待任务完成并获取结果
        int result = await task;
        
        Console.WriteLine("Sum result: " + result);
        Console.WriteLine("Main thread is done.");
    }

    static int CalculateSum(int a, int b)
    {
        Console.WriteLine("Calculating sum...");
        Thread.Sleep(2000); // 模拟工作
        return a + b;
    }
}

2.3 使用 async/await

async/await 是 C# 中用于简化异步编程的关键字,使得异步代码看起来像同步代码一样。

2.3.1 异步方法

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Main thread is running...");
        
        // 调用异步方法
        await DoWorkAsync();
        
        Console.WriteLine("Main thread is done.");
    }

    static async Task DoWorkAsync()
    {
        Console.WriteLine("Async method is starting...");
        await Task.Delay(2000); // 模拟异步工作
        Console.WriteLine("Async method is done.");
    }
}

2.3.2 返回异步结果

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Main thread is running...");
        
        // 调用异步方法并获取结果
        int result = await CalculateSumAsync(10, 20);
        
        Console.WriteLine("Sum result: " + result);
        Console.WriteLine("Main thread is done.");
    }

    static async Task<int> CalculateSumAsync(int a, int b)
    {
        Console.WriteLine("Calculating sum asynchronously...");
        await Task.Delay(2000); // 模拟异步工作
        return a + b;
    }
}

3. 线程同步

在多线程编程中,线程同步是一个重要的问题。多个线程同时访问共享资源时,可能会导致数据不一致或竞争条件。C# 提供了多种线程同步机制。

3.1 lock 语句

lock 语句用于锁定一个对象,确保同一时间只有一个线程可以访问被锁定的代码块。

using System;
using System.Threading.Tasks;

class Program
{
    private static readonly object _lock = new object();
    private static int _counter = 0;

    static async Task Main()
    {
        Task task1 = Task.Run(() => IncrementCounter());
        Task task2 = Task.Run(() => IncrementCounter());

        await Task.WhenAll(task1, task2);

        Console.WriteLine("Counter: " + _counter);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 100000; i++)
        {
            lock (_lock)
            {
                _counter++;
            }
        }
    }
}

3.2 Monitor 类

Monitor 类提供了更灵活的锁定机制,可以手动控制锁定和解锁。

using System;
using System.Threading.Tasks;

class Program
{
    private static readonly object _lock = new object();
    private static int _counter = 0;

    static async Task Main()
    {
        Task task1 = Task.Run(() => IncrementCounter());
        Task task2 = Task.Run(() => IncrementCounter());

        await Task.WhenAll(task1, task2);

        Console.WriteLine("Counter: " + _counter);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 100000; i++)
        {
            bool lockTaken = false;
            try
            {
                Monitor.Enter(_lock, ref lockTaken);
                _counter++;
            }
            finally
            {
                if (lockTaken)
                {
                    Monitor.Exit(_lock);
                }
            }
        }
    }
}

3.3 Mutex 类

Mutex 类用于跨进程的线程同步,可以确保同一时间只有一个线程可以访问共享资源。

using System;
using System.Threading;

class Program
{
    private static Mutex _mutex = new Mutex();
    private static int _counter = 0;

    static void Main()
    {
        for (int i = 0; i < 5; i++)
        {
            Thread thread = new Thread(IncrementCounter);
            thread.Start();
        }
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 100000; i++)
        {
            _mutex.WaitOne();
            _counter++;
            _mutex.ReleaseMutex();
        }
        Console.WriteLine("Counter: " + _counter);
    }
}

4. 实践练习

4.1 练习1:并行计算

编写一个程序,使用多线程并行计算一组数字的平方和。

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        int sum = await CalculateSquareSumAsync(numbers);
        Console.WriteLine("Sum of squares: " + sum);
    }

    static async Task<int> CalculateSquareSumAsync(int[] numbers)
    {
        int sum = 0;
        object _lock = new object();

        await Task.Run(() =>
        {
            Parallel.ForEach(numbers, number =>
            {
                int square = number * number;
                lock (_lock)
                {
                    sum += square;
                }
            });
        });

        return sum;
    }
}

4.2 练习2:生产者-消费者模型

编写一个程序,实现生产者-消费者模型。生产者线程生成数据并放入队列,消费者线程从队列中取出数据并处理。

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static BlockingCollection<int> _queue = new BlockingCollection<int>();

    static async Task Main()
    {
        Task producer = Task.Run(() => Producer());
        Task consumer = Task.Run(() => Consumer());

        await Task.WhenAll(producer, consumer);
    }

    static void Producer()
    {
        for (int i = 0; i < 10; i++)
        {
            _queue.Add(i);
            Console.WriteLine("Produced: " + i);
            Thread.Sleep(100);
        }
        _queue.CompleteAdding();
    }

    static void Consumer()
    {
        foreach (int item in _queue.GetConsumingEnumerable())
        {
            Console.WriteLine("Consumed: " + item);
            Thread.Sleep(200);
        }
    }
}

5. 总结

多线程编程是提高程序性能和响应性的重要手段。C# 提供了丰富的多线程编程工具,包括 Thread、Task、async/await 等。在多线程编程中,线程同步是一个关键问题,需要合理使用 lock、Monitor、Mutex 等机制来确保线程安全。

通过本教程的学习,你应该能够理解多线程编程的基本概念,掌握 C# 中的多线程编程技术,并能够编写简单的多线程程序。